from pyglet.window import mouse
from pyglet.window import key
from pyglet.gl import glEnable, glDisable, glScissor
from pyglet.gl import GL_SCISSOR_TEST

from rect import Rect


class Widget(Rect):

    _shared = {'tooltip': None, 'menu': None}

    def __init__(self, parent=None, visible=True, *args, **kwargs):
        Rect.__init__(self, *args, **kwargs)
        
        self._parent = parent
        self._visible = visible

        self._bg_view = None
        self._fg_view = None

        self._active = False
        self._frame = None

        self._clicked = {mouse.LEFT: False,
                         mouse.MIDDLE: False,
                         mouse.LEFT|mouse.MIDDLE: False,
                         mouse.RIGHT: False,
                         mouse.LEFT|mouse.RIGHT: False,
                         mouse.MIDDLE|mouse.RIGHT: False,
                         mouse.LEFT|mouse.MIDDLE|mouse.RIGHT: False}

        self._actions = {}
        for action in ('click', 'release', 'drag'):
            self._actions[action] = {}
        
        for action in ('motion', 'scroll'):
            self._actions[action] = None

        for action in ('key_press', 'key_release'):
            self._actions[action] = {}



    def connect(self, action, function, buttons=None, symbol=None,
                modifiers=0):

        if action in ('click', 'release', 'drag'):
            self._actions[action][buttons] = {modifiers: function}

        elif action in ('scroll', 'motion'):
            self._actions[action] = function

        elif action in ('key_press', 'key_release'):
            self._actions[action][symbol] = {modifiers: function}


    def disconnect(self, action, buttons=None, symbol=None, modifiers=0):

        if action in ('click', 'release', 'drag'):
            
            act = self._actions[action]
            del act[buttons][modifiers]
            
            if len(act[buttons]) == 0:
                del act[buttons]

        elif action in ('scroll', 'motion'):
            self._actions[action] = None

        elif action in ('key_press', 'key_release'):
            act = self._actions[action]
            del act[symbol][modifiers]
            
            if len(act[symbol]) == 0:
                del act[symbol]


    def register_bg(self, bg):
        self._bg_view = bg(self)

    def register_fg(self, fg):
        self._fg_view = fg(self)


    def unregister_bg(self):
        self._bg_view = None

    def unregister_fg(self):
        self._fg_view = None


    def get_visible(self):
        return self._visible

    def set_visible(self, visible):
        assert type(visible) is bool
        self._visible = visible
    visible = property(get_visible, set_visible)


    def _draw_background(self, rect=None):
        if self._bg_view:
            self._bg_view.draw(rect)

    def _draw_foreground(self, rect=None):
        if self._fg_view:
            self._fg_view.draw(rect)

    def draw(self, rect=None):
        raise AttributeError(' '.join(["This class must be overriden,",
                                       "don't use it directly"]))


    def _click_routine(self, button, modifier):
        if self._actions['click'].has_key(button) and\
           self._actions['click'][button].has_key(modifier):
            
            self._actions['click'][button][modifier]()

    def on_click(self, x, y, button, modifier):        
        if not self.point_collide(x, y):
            return False
        self._clicked[button] = True
        self._click_routine(button, modifier)
        return True
            

    def _release_routine(self, button, modifier):
        if self._actions['release'].has_key(button) and\
           self._actions['release'][button].has_key(modifier):

            self._actions['release'][button][modifier]()

    def on_release(self, x, y, button, modifier):
        if not self._clicked[button]:
            return False
        self._clicked[button] = False
        self._release_routine(button, modifier)
        return True


    def _drag_routine(self, dx, dy, buttons, modifiers):
        if self._actions['drag'].has_key(buttons) and\
           self._actions['drag'][buttons].has_key(modifiers):

            self._actions['drag'][buttons][modifiers](dx, dy)

    def on_drag(self, x, y, dx, dy, buttons, modifiers):
        if not self.point_collide(x, y) and not self._clicked[buttons]:
            return False
        self._drag_routine(dx, dy, buttons, modifiers)
        return True


    def _scroll_routine(self, scroll_x, scroll_y):
        if self._actions['scroll']:
            self._actions['scroll'](scroll_x, scroll_y)

    def on_scroll(self, x, y, scroll_x, scroll_y):
        if not self.point_collide(x, y):
            return False
        self._scroll_routine(scroll_x, scroll_y)
        return True


    def _motion_routine(self, dx, dy):
        if self._actions['motion']:
            self._actions['motion'](dx, dy)
    
    def _switch_active_off(self):
        self._active = False

    def on_motion(self, x, y, dx, dy):
        if not self.point_collide(x, y):

            self._switch_active_off()
            return False

        self._active = True
        self._motion_routine(dx, dy)
        return True


    def _key_press_routine(self, symbol, modifier):
        if self._actions['key_press'].has_key(symbol) and\
           self._actions['key_press'][symbol].has_key(modifier):
            
            self._actions['key_press'][symbol][modifier]()

    def on_key_press(self, symbol, modifier):
        if self._active:
            self._key_press_routine(symbol, modifier)
            
            return True
        return False


    def _key_release_routine(self, symbol, modifier):
        if self._actions['key_release'].has_key(symbol) and\
           self._actions['key_release'][symbol].has_key(modifier):

            self._actions['key_release'][symbol][modifier]()

    def on_key_release(self, symbol, modifier):
        if self._active:
            self._key_release_routine(symbol, modifier)

            return True
        return False


    def _resize_routine(self):
        pass

    def on_resize(self, width, height):
        self.width = width
        self.height = height

        self._resize_routine(self)


    def _register_frame(self, frame):
        self._frame = frame



class BasicContainer(Widget):

    def __init__(self, child=None, *args, **kwargs):

        Widget.__init__(self, *args, **kwargs)
        self.child = None
        if child:
            self.add_child(child)
    
    def add_child(self, child):
        if not self.child:
            self.child = child

            if self._frame:
                self.child._register_frame(self._frame)

        else:
            raise AttributeError(' '.join(['child is still set,',
                                           'remove it first, to',
                                           'add a new one']))

    def remove_child(self):
        self.child = None


    def draw(self, rect=None):

        if rect and self._visible:
            if self.clash(rect):
               
                rect = self.clip(rect)
                self._draw_background(rect)
               
                if self.child:
                    self.child.draw(rect)

                self._draw_foreground(rect)

        elif self._visible:
            self._draw_background()

            if self.child:
                self.child.draw(self)

            self._draw_foreground()


    def on_click(self, x, y, button, modifier):
        if Widget.on_click(self, x, y, button, modifier):

            if self.child:
                self.child.on_click(x, y, button, modifier)

            return True
        return False


    def on_release(self, x, y, button, modifier):
        if Widget.on_release(self, x, y, button, modifier):

            if self.child:
                self.child.on_release(x, y, button, modifier)

            return True
        return False


    def on_drag(self, x, y, dx, dy, buttons, modifiers):
        if Widget.on_drag(self, x, y, dx, dy, buttons, modifiers):

            if self.child:
                self.child.on_drag(x, y, dx, dy, buttons, modifiers)

            return True
        return False


    def on_motion(self, x, y, dx, dy):
        if Widget.on_motion(self, x, y, dx, dy):

            if self.child:
                self.child.on_motion(x, y, dx, dy)

            return True
        return False

    def _switch_active_off(self):
        Widget._switch_active_off(self)
    
        if self.child:
            self.child._switch_active_off()


    def on_scroll(self, x, y, scroll_x, scroll_y):
        if Widget.on_scroll(self, x, y, scroll_x, scroll_y):

            if self.child:
                self.child.on_scroll(x, y, scroll_x, scroll_y)

            return True
        return False


    def move_x(self, dx):
        Widget.move_x(self, dx)
        if self.child:
            self.child.move_x(dx)

    def move_y(self, dy):
        Widget.move_y(self, dy)
        if self.child:
            self.child.move_y(dy)


    def _register_frame(self, frame):
        Widget._register_frame(self, frame)
        if self.child:
            self.child._register_frame(frame)



class BasicLayout(Widget):

    def __init__(self, children=[], *args, **kwargs):
        Widget.__init__(self, *args, **kwargs)
        self.children = []
        self.add_children(children)


    def draw(self, rect=None):

        if rect and self._visible:
            if self.clash(rect):

                rect = self.clip(rect)
                self._draw_background(rect)

                for child in self.children:
                    child.draw(rect)

                self._draw_foreground(rect)

        elif self._visible:
            self._draw_background()

            for child in self.children:
                child.draw(self)

            self._draw_foreground()


    def add_child(self, child):
        self.children.append(child)
        if self._frame:
            child._register_frame(self._frame)

    def insert_child(self, nmb, child):
        self.children.insert(nmb, child)


    def add_children(self, children):
        assert type(children) in (list, tuple)
        for child in children:
            self.add_child(child)

    def remove_child(self, child=None, nmb=None):
        assert child or nmb
        if child:
            self.children.remove(child)
        else:
            del self.children[nmb]


    def move_x(self, dx):
        Widget.move_x(self, dx)
        for child in self.children:
            child.move_x(dx)

    def move_y(self, dy):
        Widget.move_y(self, dy)
        for child in self.children:
            child.move_y(dy)


    def on_click(self, x, y, button, modifier):
        if Widget.on_click(self, x, y, button, modifier):

            for child in self.children:
                child.on_click(x, y, button, modifier)
            
            return True
        return False


    def on_release(self, x, y, button, modifier):
        if Widget.on_release(self, x, y, button, modifier):

            for child in self.children:
                child.on_release(x, y, button, modifier)

            return True
        return False


    def on_drag(self, x, y, dx, dy, buttons, modifiers):
        if Widget.on_drag(self, x, y, dx, dy, buttons, modifiers):

            for child in self.children:
                child.on_drag(x, y, dx, dy, buttons, modifiers)

            return True
        return False


    def on_motion(self, x, y, dx, dy):
        if Widget.on_motion(self, x, y, dx, dy):

            for child in self.children:
                child.on_motion(x, y, dx, dy)

            return True
        return False

    def _switch_active_off(self):
        Widget._switch_active_off(self)
        for child in self.children:
            child._switch_active_off()


    def on_scroll(self, x, y, scroll_x, scroll_y):
        if Widget.on_scroll(self, x, y, scroll_x, scroll_y):

            for child in self.children:
                child.on_scroll(x, y, scroll_x, scroll_y)

            return True
        return False


    def _register_frame(self, frame):
        Widget._register_frame(self, frame)

        for child in self.children:
            child._register_frame(frame)



class BasicWrapper(Widget):

    def add_child(self, child, child_width, child_height):


        if self.xstick == 'left':
            child.x = self.left

        elif self.xstick == 'right':
            child.x = self.right - child_width


        if self.ystick == 'bottom':
            child.y = self.bottom

        elif self.ystick == 'top':
            child.y = self.top - child_height

        self.width = child_width
        self.height = child_height

        self.child = child


    def draw(self, rect=None):

        if rect and self._visible:
            if self.clash(rect):

                rect = self.clip(rect)
                self._draw_background(rect)

                if self.child:
                    glEnable(GL_SCISSOR_TEST)
                    glScissor(rect.left, rect.bottom,
                              rect.width, rect.height)

                    self.child.draw()
                    glDisable(GL_SCISSOR_TEST)

                self._draw_foreground(rect)

        elif self._visible:
            self._draw_background()

            if self.child:
                self.child.draw()

            self._draw_foreground()


    def move_x(self, dx):
        Widget.move_x(self, dx)
        self.child.x += dx


    def move_y(self, dy):
        Widget.move_y(self, dy)
        self.child.y += dy
